package com.freemalll.merchant.service.impl.merchantAuditManage;

import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.freemalll.common.core.context.SpringContextHolder;
import com.freemalll.common.core.exception.BusinessException;
import com.freemalll.common.security.utils.SecurityUtils;
import com.freemalll.merchant.common.CommonEnum;
import com.freemalll.merchant.common.Constant;
import com.freemalll.merchant.common.cache.CacheContext;
import com.freemalll.merchant.constant.ConfigConstant;
import com.freemalll.merchant.domain.dto.*;
import com.freemalll.merchant.domain.entity.*;
import com.freemalll.merchant.domain.entity.coopManagement.MerAssetManage;
import com.freemalll.merchant.domain.vo.*;
import com.freemalll.merchant.mapper.merchantAuditManage.MerMerchantAuditEditLogMapper;
import com.freemalll.merchant.mapper.merchantAuditManage.MerchantAuditManageMapper;
import com.freemalll.merchant.service.*;
import com.freemalll.merchant.service.coopManagement.MerAssetManageService;
import com.freemalll.merchant.service.merchantAuditManage.MerAuthFlowLogService;
import com.freemalll.merchant.service.merchantAuditManage.MerAuthFlowService;
import com.freemalll.merchant.service.merchantAuditManage.MerMerchantAuditEditLogService;
import com.freemalll.merchant.service.merchantAuditManage.MerchantAuditManageService;
import com.freemalll.merchant.utils.*;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * @author songshixiong
 * @ClassName MerchantAuditManageServiceImpl
 * @description:
 * @date 2025年05月28日
 * @version: 1.0
 */

@Service
@AllArgsConstructor
public class MerchantAuditManageServiceImpl implements MerchantAuditManageService {

    private final MerLicenseService merLicenseService;
    private final MerLegalInfoService merLegalInfoService;
    private final MerAccountLicenseService merAccountLicenseService;
    private final MerMedicalLicenseService merMedicalLicenseService;
    private final MerVenueInfoService merVenueInfoService;
    private final MerRunInfoService merRunInfoService;
    private final MerAttachService merAttachService;
    private final MerAttachValueService merAttachValueService;
    private final MerInstallmentInfoService merInstallmentInfoService;
    private final MerBankCardService merBankCardService;
    private final MerInstallmentInfoCompetitiveService merInstallmentInfoCompetitiveService;
    private final MerMerchantRateManageService merMerchantRateManageService;
    private final MerMerchantCapitalOrderService merMerchantCapitalOrderService;
    private final MerMerchantService merMerchantService;
    private final MerMerchantAuditEditLogService merMerchantAuditEditLogService;
    private final HistoryDiffUtils historyDiffUtils;
    private final MerchantAuditManageMapper merchantAuditManageMapper;
    private final RiskEgService riskEgService;
    private final MerAuthFlowLogService merAuthFlowLogService;
    private final MerAuthFlowService merAuthFlowService;
    private final MerMerchantRegisterStepService merMerchantRegisterStepService;
    private final MerMerchantAuditEditLogMapper merMerchantAuditEditLogMapper;
    private final MerParamsService merParamsService;
    private final MerMedicalEduLicenseService merMedicalEduLicenseService;
    private final MerMedicalEduIcpLicenseService merMedicalEduIcpLicenseService;
    private final AliOSSUtil aliOSSUtil;
    private final MerAttachConfigService merAttachConfigService;
    private final MerMerchantRateManageDetailService merMerchantRateManageDetailService;
    private final RedisUtil redisUtil;
    private final MerCooperateHistoryService merCooperateHistoryService;
    private final CacheContext cacheContext;

    // 日志内容前缀策略
    private static final Map<Class<?>, BiFunction<Object, Object, String>> LOG_PREFIX_STRATEGY = new HashMap<>();

    static {
        LOG_PREFIX_STRATEGY.put(MerMerchantRateManage.class, (oldDto, unused) -> {
            if (oldDto instanceof MerMerchantRateManage) {
                return Optional.ofNullable(((MerMerchantRateManage) oldDto).getSourceId())
                        .map(id -> {
                            MerAssetManage asset = SpringContextHolder.getBean(MerAssetManageService.class).getById(id);
                            return asset != null && asset.getSourceName() != null ? "(" + asset.getSourceName() + ")" : "";
                        }).orElse("");
            }
            return "";
        });
        LOG_PREFIX_STRATEGY.put(MerBankCard.class, (oldDto, unused) -> {
            if (oldDto instanceof MerBankCard) {
                String bankName = ((MerBankCard) oldDto).getCardBankName();
                return bankName != null ? "(" + bankName + ")" : "";
            }
            return "";
        });
        LOG_PREFIX_STRATEGY.put(MerMerchantRateManageDetail.class, (oldDto, unused) -> {
            if (oldDto instanceof MerMerchantRateManageDetail) {
                MerMerchantRateManageDetail detail = (MerMerchantRateManageDetail) oldDto;
                String timeName = detail.getTimes() != null ? "(期数" + detail.getTimes() + ")" : "";
                String sourceName = "";
                String merRateId = detail.getMerRateId();
                if (StringUtils.hasText(merRateId)) {
                    MerMerchantRateManage rateManage = SpringContextHolder.getBean(MerMerchantRateManageService.class).getById(merRateId);
                    if (rateManage != null && StringUtils.hasText(rateManage.getSourceId())) {
                        MerAssetManage assetManage = SpringContextHolder.getBean(MerAssetManageService.class).getById(rateManage.getSourceId());
                        if (assetManage != null && StringUtils.hasText(assetManage.getSourceName())) {
                            sourceName = "(" + assetManage.getSourceName() + ")";
                        }
                    }
                }
                return sourceName + timeName;
            }
            return "";
        });
        // 可继续扩展其他类型
    }


    @Override
    public MerchantAuditVo getMerInfoById(String merId, String listType) {
        MerchantAuditVo merchantAuditVo = new MerchantAuditVo();
        //校验权限和绑定审核人
        checkAuth(merId, listType, merchantAuditVo);
        //查询商户信息
        merchantAuditVo.setMerchantAuditDetail(getMerchantAuditDetail(merId));
        //证照信息
        merchantAuditVo.setBusinessLicense(merLicenseService.getBusinessLicenseById(merId));
        //法人信息
        merchantAuditVo.setCorporateInfo(merLegalInfoService.getCorporateInfoById(merId));
        // 执行许可大健康
        merchantAuditVo.setMedicalLicense(merMedicalLicenseService.getMedicalLicenseById(merId));
        // 执行许可教育
        merchantAuditVo.setMerMedicalEduLicense(
                Optional.ofNullable(merMedicalEduLicenseService.getEduLicenseById(merId))
                        .map(vo -> {
                            vo.setSchoolTypeName(Optional.ofNullable(CommonEnum.schoolType.fromDescription(vo.getSchoolType()))
                                    .map(CommonEnum.schoolType::getDescription)
                                    .orElse(null));
                            return vo;
                        })
                        .orElse(null)
        );
        merchantAuditVo.setMerMedicalEduIcpLicense(merMedicalEduIcpLicenseService.getEduIcpLicenseById(merId));
        // 产权信息
        merchantAuditVo.setVenueInfo(merVenueInfoService.getVenueInfoById(merId));
        //开户信息
        merchantAuditVo.setAccountLicense(BeanUtils.convert(merAccountLicenseService.getOne(Wrappers.<MerAccountLicense>lambdaQuery().eq(MerAccountLicense::getMerId, merId)), MerAccountLicenseDto.class));
        //审核补件清单
        List<MerAttachCheckVO> merAttachCheckVOlist = merAttachService.listCheckListByMerId(merId);
        merchantAuditVo.setMerAttachCheckList(merAttachCheckVOlist);
        //经营信息
        merchantAuditVo.setMerBusinessInfo(Optional.ofNullable(merRunInfoService.getOne(Wrappers.<MerRunInfo>lambdaQuery().eq(MerRunInfo::getMerId, merId)))
                .map(runInfo -> {
                    MerBusinessInfoDto dto = BeanUtils.convert(runInfo, MerBusinessInfoDto.class);
                    Optional.ofNullable(dto.getOnlinePlatform()).ifPresent(platform -> dto.setOnlinePlatformName(merParamsService.translateParamKeys(platform)));
                    Optional.ofNullable(dto.getProjectList()).ifPresent(project -> dto.setProjectListName(merParamsService.translateParamKeys(project)));
                    Optional.ofNullable(dto.getAdvertisement()).ifPresent(ad -> dto.setAdvertisementName(merParamsService.translateParamKeys(ad)));
                    return dto;
                })
                .orElse(null));
        //获取所有当前以及历史附件,返回给前端特定格式数据map
        List<MerAttachDto> list = merAttachService.listHistoryByMerId(merId);

        merchantAuditVo.setMerAttachList(packageHistoryData(list, merId));
        //商户Logo
        list.stream()
                .filter(merAttach -> "current".equals(merAttach.getNode()) && "merchant_logo".equals(merAttach.getConfigCode()))
                .findFirst()
                .ifPresent(merAttach -> {
                    if (merAttach.getAttachResultList() != null && !merAttach.getAttachResultList().isEmpty()) {
                        merchantAuditVo.setLogoValue(merAttach.getAttachResultList().get(0));
                    }
                });
        //分期信息
        MerInstallmentInfoDto installmentInfoDto = BeanUtils.convert(merInstallmentInfoService.getOne(Wrappers.<MerInstallmentInfo>lambdaQuery().eq(MerInstallmentInfo::getMerId, merId)), MerInstallmentInfoDto.class);
        if (installmentInfoDto != null) {
            installmentInfoDto.setFirstRecommendedPlatformName(merParamsService.translateParamKeys(installmentInfoDto.getFirstRecommendedPlatform()));
            installmentInfoDto.setCompetitiveProductTypesName(merParamsService.translateParamKeys(installmentInfoDto.getCompetitiveProductTypes()));
            List<MerInstallmentInfoCompetitiveDto> competitiveList = BeanUtils.beanToBeanList(merInstallmentInfoCompetitiveService.list(Wrappers.<MerInstallmentInfoCompetitive>lambdaQuery().eq(MerInstallmentInfoCompetitive::getInstallmentId, installmentInfoDto.getInstallmentId())), MerInstallmentInfoCompetitiveDto.class);
            if (competitiveList != null) {
                competitiveList.forEach(item -> item.setCompetitiveOrganizationsName(merParamsService.translateParamKeys(item.getCompetitiveOrganizations())));
            }
            installmentInfoDto.setCompetitiveList(competitiveList);
        }

        merchantAuditVo.setInstallmentInfo(installmentInfoDto);
        //收款户信息
        List<MerBackCardDto> backCardDtos = merBankCardService.listByMerId(merId);
        merchantAuditVo.setBackCardList(backCardDtos);
        //资方顺位
        List<MerMerchantCapitalOrderDto> merchantCapitalOrderDtos = merMerchantCapitalOrderService.listByMerId(merId);
        merchantAuditVo.setCapitalOrderList(merchantCapitalOrderDtos);
        //费率
        List<MerchantRateDto> merchantRateDtos = merMerchantRateManageService.listByMerId(merId, merchantAuditVo.getMerchantAuditDetail().getSceneParentCategory());
        merchantAuditVo.setSourceInfoList(merchantRateDtos);
        return merchantAuditVo;
    }

    private void checkAuth(String merId, String listType, MerchantAuditVo merchantAuditVo) {
        String userId = SecurityUtils.getUserId();
        Set<String> buttons = cacheContext.getButtons(userId);
        String auditViewPermissionConfig = ConfigUtil.getCoValue(ConfigConstant.AUDIT_VIEW_PERMISSION_CONFIG);
        //判断是否是查看权限 是则跳过绑定直接设置为不可编辑
        if (buttons.contains(auditViewPermissionConfig)) {
            return;
        }
        //待审核状态绑定当前访问用户为审核人
        checkUserStatue(merId, listType);

        MerAuthFlow finalFlow = merAuthFlowService.getOne(Wrappers.<MerAuthFlow>lambdaQuery().eq(MerAuthFlow::getMerId, merId));
        //判断商户的审核人员是否当前登录人员 是则可编辑
        if (Objects.equals(finalFlow.getCurrentAuthManId(), userId)) {
            merchantAuditVo.setEditBtnShowFlag(true);
        }
    }

    private void checkUserStatue(String merId, String listType) {
        String userId = SecurityUtils.getUserId();
        MerAuthFlow flow = merAuthFlowService.getOne(Wrappers.<MerAuthFlow>lambdaQuery().eq(MerAuthFlow::getMerId, merId));
        if (flow == null) {
            return;
        }
        if (!CommonEnum.auditType.PENDING_APPROVAL.name().equals(flow.getAuthStatus())) {
            return;
        }
        // 分布式锁的Key：基于商户ID，避免不同商户互相阻塞
        String lockKey = Constant.AUDIT_CENTER_AUDITOR + merId;
        try {
            // 加锁 最多2秒 启用看门狗机制，防止死锁
            Boolean tryLock = redisUtil.tryLock(lockKey, 1, TimeUnit.SECONDS);
            if (Boolean.FALSE.equals(tryLock)) {
                throw BusinessException.businessOther("系统繁忙，请稍后重试");
            }
            // 双重校验：重新查询最新状态
            MerAuthFlow latestFlow = merAuthFlowService.getById(flow.getFlowId());
            if (!CommonEnum.auditType.PENDING_APPROVAL.name().equals(latestFlow.getAuthStatus())) {
                throw BusinessException.businessOther("审核状态已变更，请刷新重试");
            }
            // 更新审核人信息
            latestFlow.setCurrentAuthMan(SecurityUtils.getUsername());
            latestFlow.setCurrentAuthManId(userId);
            latestFlow.setCurrentAuthDate(LocalDateTime.now());
            latestFlow.setAuthStatus(CommonEnum.auditType.APPROVING.name());
            // 乐观锁更新
            boolean success = merAuthFlowService.updateById(latestFlow);
            if (!success) {
                throw BusinessException.businessOther("系统繁忙，请稍后重试");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw BusinessException.businessOther("系统繁忙，请稍后重试");
        } finally {
            // 释放锁（仅当锁持有者是当前线程时）
            if (redisUtil.isLocked(lockKey)) {
                redisUtil.unlock(lockKey);
            }
        }

        //如果是走首页进入的审核中心则不校验权限，直接返回数据
        if (Objects.equals(CommonEnum.auditCenterType.HOME_PAGE.name(), listType)) {
            return;
        }
        // 最终权限校验（无锁状态，仅读校验）
        MerAuthFlow finalFlow = merAuthFlowService.getById(flow.getFlowId());
        if (!userId.equals(finalFlow.getCurrentAuthManId())) {
            throw BusinessException.businessOther("当前案件已归属于" + finalFlow.getCurrentAuthManId() + ",无法操作");
        }
    }


    public Map<String, List<Map<String, Object>>> packageHistoryData(List<MerAttachDto> merAttachList, String merId) {
        // 先处理每个 MerAttachDto 的业务逻辑
        MerAuthFlow flow = merAuthFlowService.getOne(Wrappers.<MerAuthFlow>lambdaQuery().eq(MerAuthFlow::getMerId, merId));
        Integer supplementNum = flow.getSupplementNum();
        merAttachList.forEach(merAttach -> {
            if (supplementNum != 0 && "合格".equals(merAttach.getAttachResult())) {
                merAttach.setIfEdit(0);
            } else {
                merAttach.setIfEdit(1);
            }

            if (merAttach.getAttachType() != null) {
                if (merAttach.getAttachType() == 2) {
                    if ("合格".equals(merAttach.getAttachResult())) {
                        merAttach.setAttachResultCode("1");
                    } else if ("补件".equals(merAttach.getAttachResult())) {
                        merAttach.setAttachResultCode("0");
                    } else if ("拒绝合作".equals(merAttach.getAttachResult())) {
                        merAttach.setAttachResultCode("2");
                    }
                }
                if (merAttach.getAttachType() == 1 && "补件".equals(merAttach.getAttachResult())) {
                    merAttach.setAttachResult("不合格");
                } else if (merAttach.getAttachType() == 2 && "合格".equals(merAttach.getAttachResult())) {
                    merAttach.setAttachResult("无异常");
                } else if (merAttach.getAttachType() == 3 && "补件".equals(merAttach.getAttachResult())) {
                    merAttach.setAttachResult("不合格");
                }
            }

            merAttach.setAttachResultList(
                    merAttachValueService.list(
                                    Wrappers.<MerAttachValue>lambdaQuery()
                                            .eq(MerAttachValue::getAttachId, merAttach.getAttachId())
                            )
                            .stream()
                            .map(MerAttachValue::getValValue)
                            .filter(Objects::nonNull)
                            .map(aliOSSUtil::getFileUrl)
                            .collect(Collectors.toList())
            );
        });

        // 按 configCode 分组
        Map<String, List<MerAttachDto>> groupedByConfigCode = merAttachList.stream()
                .collect(Collectors.groupingBy(MerAttachDto::getConfigCode));

        // 创建结果Map
        Map<String, List<Map<String, Object>>> resultMap = new LinkedHashMap<>();

        // 定义需要排序的configCode及其顺序
        List<String> priorityOrder = Arrays.asList(
                "business_license",
                "legal_id_card_front",
                "legal_id_card_back",
                "practice_license",
                "education_license",
                "icp_license",
                "lease_agreement",
                "account_opening_license"
        );

        // 初始化分组列表（使用LinkedHashMap保持顺序）
        Map<String, List<Map<String, Object>>> tempGroupMap = new LinkedHashMap<>();
        tempGroupMap.put("group1", new ArrayList<>());
        tempGroupMap.put("group2", new ArrayList<>());
        tempGroupMap.put("group3", new ArrayList<>());
        tempGroupMap.put("group4", new ArrayList<>());

        // 处理每个分组
        groupedByConfigCode.forEach((configCode, group) -> {
            Map<String, Object> configResult = new HashMap<>();
            configResult.put("configCode", configCode);

            String configName = group.isEmpty() ? null : group.get(0).getConfigName();
            configResult.put("configName", configName);

            Integer attachType = group.isEmpty() ? null : group.get(0).getAttachType();

            configResult.put("current", group.stream()
                    .filter(attach -> "current".equals(attach.getNode()))
                    .map(this::convertToMap).findFirst()
            );

            configResult.put("first", group.stream()
                    .filter(attach -> "first".equals(attach.getNode()))
                    .map(this::convertToMap).findFirst());

            configResult.put("second", group.stream()
                    .filter(attach -> "second".equals(attach.getNode()))
                    .map(this::convertToMap).findFirst());

            // 根据 attachType 分配到不同的组
            if (attachType != null) {
                switch (attachType) {
                    case 1:
                        tempGroupMap.get("group1").add(configResult);
                        break;
                    case 2:
                        tempGroupMap.get("group2").add(configResult);
                        break;
                    case 3:
                        tempGroupMap.get("group3").add(configResult);
                        break;
                    default:
                        tempGroupMap.get("group4").add(configResult);
                }
            } else {
                tempGroupMap.get("group4").add(configResult);
            }
        });

        // 先添加按指定顺序排序的configCode
        for (String code : priorityOrder) {
            if (groupedByConfigCode.containsKey(code)) {
                tempGroupMap.forEach((groupName, groupList) -> {
                    List<Map<String, Object>> filtered = groupList.stream()
                            .filter(m -> code.equals(m.get("configCode")))
                            .collect(Collectors.toList());

                    if (!filtered.isEmpty()) {
                        resultMap.computeIfAbsent(groupName, k -> new ArrayList<>()).addAll(filtered);
                    }
                });
            }
        }

        // 再添加未指定的configCode（保持原有分组）
        tempGroupMap.forEach((groupName, groupList) -> {
            List<Map<String, Object>> remaining = groupList.stream()
                    .filter(m -> !priorityOrder.contains(m.get("configCode")))
                    .collect(Collectors.toList());

            if (!remaining.isEmpty()) {
                resultMap.computeIfAbsent(groupName, k -> new ArrayList<>()).addAll(remaining);
            }
        });

        return resultMap;
    }

    // 辅助方法：将 MerAttach 转换为 Map
    private Map<String, Object> convertToMap(MerAttachDto attach) {
        Map<String, Object> map = new HashMap<>();
        map.put("attachId", attach.getAttachId());
        map.put("attachType", attach.getAttachType());

        map.put("merId", attach.getMerId());
        map.put("configName", attach.getConfigName());
        map.put("attachResultList", attach.getAttachResultList());
        map.put("attachResult", attach.getAttachResult());
        map.put("attachResultCode", attach.getAttachResultCode());

        map.put("remark", attach.getRemark());
        map.put("eventData", attach.getEventData());

        map.put("riskLevel", attach.getRiskLevel());
        map.put("eventDetail", attach.getEventDetail());
        map.put("opinion", attach.getOpinion());
        map.put("isEdit", attach.getIfEdit());


        return map;
    }

    //获取需要补件的附件，以及附件详情
    public HistoryDeletedListVO getSupplementAttachInfoList(String merId) {
        List<MerAttachDto> attachList = BeanUtils.beanToBeanList(merAttachService.list(Wrappers.<MerAttach>lambdaQuery().eq(MerAttach::getMerId, merId)), MerAttachDto.class);

        // 新增：遍历attachList，通过configCode查mer_attach_config表，set configMax/configMin
        for (MerAttachDto attach : attachList) {
            if (attach.getConfigCode() != null) {
                MerAttachConfig config = merAttachConfigService.getOne(Wrappers.<MerAttachConfig>lambdaQuery().eq(MerAttachConfig::getConfigCode, attach.getConfigCode()));
                if (config != null) {
                    attach.setConfigMax(config.getConfigMax());
                    attach.setConfigMin(config.getConfigMin());
                }
            }
        }

        Map<String, List<MerAttachDto>> grouped = attachList.stream()
                .collect(Collectors.groupingBy(MerAttachDto::getConfigCode, LinkedHashMap::new, Collectors.toList()));

        List<MerAttachDto> listFirst = new ArrayList<>();
        List<MerAttachDto> listSecond = new ArrayList<>();

        String status = null;
        MerAuthFlow flow = merAuthFlowService.getOne(Wrappers.<MerAuthFlow>lambdaQuery().eq(MerAuthFlow::getMerId, merId));
        if (flow != null) status = flow.getAuthStatus();

        for (List<MerAttachDto> group : grouped.values()) {
            group.sort(Comparator.comparing(MerAttachDto::getAddTime)); // 升序
            if ("SUPPLEMENTARY_PARTS".equals(status)) {
                // 每组最新且attach_result为补件的数据放listSecond（不查详情）
                MerAttachDto newest = group.get(group.size() - 1);
                MerAttachDto oldset = new MerAttachDto();
                BeanUtils.copyPropertiesIgnoreNull(newest, oldset);
                oldset.setAttachResultList(null);
                if ("补件".equals(oldset.getAttachResult())) {
                    listSecond.add(oldset);
                }
                group.stream().filter(item -> item.getSupplementCount() != null && item.getSupplementCount() == 1)
                        .forEach(item -> {
                            List<MerAttachValue> values = merAttachValueService.list(
                                    Wrappers.<MerAttachValue>lambdaQuery().eq(MerAttachValue::getAttachId, item.getAttachId()));
                            item.setAttachResultList(values.stream().map(MerAttachValue::getValValue).collect(Collectors.toList()));
                            listFirst.add(item);
                        });

            } else {
                group.stream().filter(item -> item.getSupplementCount() != null && item.getSupplementCount() == 2)
                        .forEach(item -> {
                            List<MerAttachValue> values = merAttachValueService.list(
                                    Wrappers.<MerAttachValue>lambdaQuery().eq(MerAttachValue::getAttachId, item.getAttachId()));
                            item.setAttachResultList(values.stream().map(MerAttachValue::getValValue).collect(Collectors.toList()));
                            listSecond.add(item);
                        });
                group.stream().filter(item -> item.getSupplementCount() != null && item.getSupplementCount() == 1)
                        .forEach(item -> {
                            List<MerAttachValue> values = merAttachValueService.list(
                                    Wrappers.<MerAttachValue>lambdaQuery().eq(MerAttachValue::getAttachId, item.getAttachId()));
                            item.setAttachResultList(values.stream().map(MerAttachValue::getValValue).collect(Collectors.toList()));
                            listFirst.add(item);
                        });
            }


        }

        HistoryDeletedListVO vo = new HistoryDeletedListVO();
        vo.setListFirst(listFirst);
        vo.setListSecond(listSecond);
        return vo;
    }


    @Override
    public void editMerchantInfo(MerAttachDto vo) {
        // 使用正确的查询条件
        MerAttach attach = merAttachService.getOne(Wrappers.<MerAttach>lambdaQuery().eq(MerAttach::getMerId, vo.getMerId()).eq(MerAttach::getConfigCode, vo.getConfigCode()));
        if (attach == null) {
            throw BusinessException.businessDataNotExist("该商户没有附件信息");
        }
        // 执行更新
        BeanUtils.copyPropertiesIgnoreNull(vo, attach);
        boolean updated = merAttachService.updateById(attach);
        if (!updated) {
            throw BusinessException.businessOther("附件信息更新失败");
        }
    }


    @Override
    public void editMerchantInfo(MerchantAuditDetailVo vo) {
        MerMerchant merchant = merMerchantService.getById(vo.getMerId());
        MerMerchant oldDto = new MerMerchant();
        BeanUtils.copyPropertiesIgnoreNull(merchant, oldDto);
        BeanUtils.copyPropertiesIgnoreNull(vo, merchant);
        merMerchantService.updateById(merchant);
        saveLog(merchant, oldDto, vo.getMerId(), CommonEnum.merchantEditLogType.MERCHANT_DETAILS.name());
    }


    @Override
    public void editBusinessLicense(BusinessLicenseDto dto) {
        MerLicense merLicense = merLicenseService.getOne(Wrappers.<MerLicense>lambdaQuery().eq(MerLicense::getMerId, dto.getMerId()));
        if (merLicense == null) {
            throw BusinessException.businessDataNotExist("该商户没有营业执照信息");
        }
        //校验有效期
        if (StringUtils.hasText(dto.getLicenseValidateStart())) {
            LocalDate startTime = LocalDate.parse(dto.getLicenseValidateStart());
            LocalDate now = LocalDate.now();
            if (startTime.isAfter(now)) {
                throw BusinessException.businessValidateError("营业执照开始日期不能大于今天");
            }
        }
        if (StringUtils.hasText(dto.getLicenseValidateEnd()) && !Objects.equals("长期", dto.getLicenseValidateEnd())) {
            LocalDate endTime = LocalDate.parse(dto.getLicenseValidateEnd());
            LocalDate now = LocalDate.now();
            if (endTime.isBefore(now)) {
                throw BusinessException.businessValidateError("营业执照结束日期不能小于今天");
            }
        }
        MerLegalInfo merLegalInfo = merLegalInfoService.getOne(Wrappers.<MerLegalInfo>lambdaQuery().eq(MerLegalInfo::getMerId, dto.getMerId()));
        if (merLegalInfo == null) {
            throw BusinessException.businessDataNotExist("该商户没有法人信息");
        }
        //法人身份证和营业执照法人身份证名称必须一致
        if (!Objects.equals(dto.getLicenseLegalMan(), merLegalInfo.getLegalMan())) {
            throw BusinessException.businessValidateError("法人身份证信息与营业执照法人信息不一致");
        }

        //校验商户名称/简称/营业执照编码
        merLicenseService.checkMerchant(dto.getLicenseCode(), dto.getLicenseName(), dto.getLicenseShortName(), true, dto.getMerId());
        merLicenseService.checkLicenseCode(dto.getLicenseCode(), dto.getLicenseName(), dto.getLicenseShortName(), true, dto.getMerId());
        MerLicense oldDto = new MerLicense();
        BeanUtils.copyPropertiesIgnoreNull(merLicense, oldDto);
        BeanUtils.copyPropertiesIgnoreNull(dto, merLicense);
        merLicenseService.updateById(merLicense);
        saveLog(merLicense, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.LICENSE_INFO.name());
    }

    @Override
    public void editCorporateInfo(CorporateInfoDto dto) {
        MerLegalInfo merLegalInfo = merLegalInfoService.getOne(Wrappers.<MerLegalInfo>lambdaQuery().eq(MerLegalInfo::getMerId, dto.getMerId()));
        if (merLegalInfo == null) {
            throw BusinessException.businessDataNotExist("该商户没有法人信息");
        }
        if (StringUtils.hasText(dto.getLegalValidateStart())) {
            LocalDate startTime = LocalDate.parse(dto.getLegalValidateStart());
            LocalDate now = LocalDate.now();
            if (startTime.isAfter(now)) {
                throw BusinessException.businessValidateError("法人身份证有效期开始日期不能大于今天");
            }
        }
        if (StringUtils.hasText(dto.getLegalValidateEnd()) && !Objects.equals("长期", dto.getLegalValidateEnd())) {
            LocalDate endTime = LocalDate.parse(dto.getLegalValidateEnd());
            LocalDate now = LocalDate.now();
            if (endTime.isBefore(now)) {
                throw BusinessException.businessValidateError("法人身份证有限期结束日期不能小于今天");
            }
        }
        MerLicense merLicense = merLicenseService.getOne(Wrappers.<MerLicense>lambdaQuery().eq(MerLicense::getMerId, dto.getMerId()));
        if (merLicense == null) {
            throw BusinessException.businessDataNotExist("该商户没有营业执照信息");
        }
        //法人身份证和营业执照法人身份证名称必须一致
        if (!Objects.equals(merLicense.getLicenseLegalMan(), dto.getLegalMan())) {
            throw BusinessException.businessValidateError("法人身份证信息与营业执照法人信息不一致");
        }

        MerLegalInfo oldDto = new MerLegalInfo();
        BeanUtils.copyPropertiesIgnoreNull(merLegalInfo, oldDto);
        BeanUtils.copyPropertiesIgnoreNull(dto, merLegalInfo);
        merLegalInfoService.updateById(merLegalInfo);
        saveLog(merLegalInfo, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.CORPORATE_INFO.name());
    }

    @Override
    public void editMedicalLicense(MedicalLicenseDto dto) {
        MerMedicalLicense merMedicalLicense = merMedicalLicenseService.getOne(Wrappers.<MerMedicalLicense>lambdaQuery().eq(MerMedicalLicense::getMerId, dto.getMerId()));
        if (merMedicalLicense == null) {
            throw BusinessException.businessDataNotExist("该商户没有执业许可证信息");
        }
        if (StringUtils.hasText(dto.getMedlValidateStart())) {
            LocalDate startTime = LocalDate.parse(dto.getMedlValidateStart());
            LocalDate now = LocalDate.now();
            if (startTime.isAfter(now)) {
                throw BusinessException.businessValidateError("执照有效期开始日期不能大于今天");
            }
        }
        if (StringUtils.hasText(dto.getMedlValidateEnd()) && !Objects.equals("长期", dto.getMedlValidateEnd())) {
            LocalDate endTime = LocalDate.parse(dto.getMedlValidateEnd());
            LocalDate now = LocalDate.now();
            if (endTime.isBefore(now)) {
                throw BusinessException.businessValidateError("执照有效期结束日期不能小于今天");
            }
        }
        MerMedicalLicense oldDto = new MerMedicalLicense();
        BeanUtils.copyPropertiesIgnoreNull(merMedicalLicense, oldDto);
        BeanUtils.copyPropertiesIgnoreNull(dto, merMedicalLicense);
        merMedicalLicenseService.updateById(merMedicalLicense);
        saveLog(merMedicalLicense, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.PRACTICE_LICENSE.name());
    }

    @Override
    public void editMerVenueInfo(MerVenueInfoDto dto) {

        MerVenueInfo venueInfo = merVenueInfoService.getOne(Wrappers.<MerVenueInfo>lambdaQuery().eq(MerVenueInfo::getMerId, dto.getMerId()));

        if (venueInfo == null) {
            throw BusinessException.businessDataNotExist("该商户没有经营场所信息");
        }

        if (dto.getRentalType() == 1) {

            if (StringUtils.hasText(dto.getVenueDateStart())) {
                LocalDate startTime = LocalDate.parse(dto.getVenueDateStart());
                LocalDate now = LocalDate.now();
                if (startTime.isAfter(now)) {
                    throw BusinessException.businessValidateError("租赁期限开始日期不能大于今天");
                }
            }
            if (StringUtils.hasText(dto.getVenueDateEnd()) && !Objects.equals("长期", dto.getVenueDateEnd())) {
                LocalDate endTime = LocalDate.parse(dto.getVenueDateEnd());
                LocalDate now = LocalDate.now();
                if (endTime.isBefore(now)) {
                    throw BusinessException.businessValidateError("租赁期限结束日期不能小于今天");
                }
            }
        }
        MerVenueInfo oldDto = new MerVenueInfo();
        BeanUtils.copyPropertiesIgnoreNull(venueInfo, oldDto);
        BeanUtils.copyPropertiesIgnoreNull(dto, venueInfo);
        merVenueInfoService.updateById(venueInfo);

        saveLog(venueInfo, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.PLACE_OF_BUSINESS.name());

    }

    @Override
    public void editMerAccountLicense(MerAccountLicenseDto dto) {
        MerAccountLicense accountLicense = merAccountLicenseService.getOne(Wrappers.<MerAccountLicense>lambdaQuery().eq(MerAccountLicense::getMerId, dto.getMerId()));

        if (accountLicense == null) {
            throw BusinessException.businessDataNotExist("该商户没有开户许可证信息");
        }
        if (StringUtils.hasText(dto.getAcclOpenDate())) {
            LocalDate startTime = LocalDate.parse(dto.getAcclOpenDate());
            LocalDate now = LocalDate.now();
            if (startTime.isAfter(now)) {
                throw BusinessException.businessValidateError("开户日期不能大于今天");
            }
        }
        MerAccountLicense oldDto = new MerAccountLicense();
        BeanUtils.copyPropertiesIgnoreNull(accountLicense, oldDto);
        BeanUtils.copyPropertiesIgnoreNull(dto, accountLicense);
        merAccountLicenseService.updateById(accountLicense);

        saveLog(accountLicense, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.ACCOUNT_OPENING_INFO.name());

    }

    @Override
    public void editMerBusinessInfo(MerBusinessInfoDto dto) {
        MerRunInfo merRunInfo = merRunInfoService.getOne(Wrappers.<MerRunInfo>lambdaQuery().eq(MerRunInfo::getMerId, dto.getMerId()));
        if (merRunInfo == null) {
            throw BusinessException.businessDataNotExist("该商户没有经营信息");
        }
        MerRunInfo oldDto = new MerRunInfo();
        BeanUtils.copyPropertiesIgnoreNull(merRunInfo, oldDto);
        BeanUtils.copyPropertiesIgnoreNull(dto, merRunInfo);
        merRunInfoService.updateById(merRunInfo);
        saveLog(merRunInfo, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.BUSINESS_INFO.name());

    }

    @Override
    public void editMerInstallmentInfo(MerInstallmentInfoDto dto) {

        MerInstallmentInfo installmentInfo = merInstallmentInfoService.getOne(Wrappers.<MerInstallmentInfo>lambdaQuery().eq(MerInstallmentInfo::getMerId, dto.getMerId()));

        if (installmentInfo == null) {
            throw BusinessException.businessDataNotExist("该商户没有分期信息");
        }
        //操作主信息并记录日志
        MerInstallmentInfo oldDto = new MerInstallmentInfo();
        BeanUtils.copyPropertiesIgnoreNull(installmentInfo, oldDto);
        BeanUtils.copyPropertiesIgnoreNull(dto, installmentInfo);
        merInstallmentInfoService.updateById(installmentInfo);
        saveLog(installmentInfo, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.INSTALLMENT_INFO.name());


        if (!CollectionUtils.isEmpty(dto.getCompetitiveList())) {
            // 拼接机构:费率字符串
            List<MerInstallmentInfoCompetitive> listOld = merInstallmentInfoCompetitiveService.list(Wrappers.<MerInstallmentInfoCompetitive>lambdaQuery().eq(MerInstallmentInfoCompetitive::getInstallmentId, installmentInfo.getInstallmentId()).orderByAsc(MerInstallmentInfoCompetitive::getCompetitiveOrganizations));
            merInstallmentInfoCompetitiveService.remove(Wrappers.<MerInstallmentInfoCompetitive>lambdaQuery().eq(MerInstallmentInfoCompetitive::getInstallmentId, installmentInfo.getInstallmentId()));
            merInstallmentInfoCompetitiveService.batchInsert(dto.getCompetitiveList(), installmentInfo.getInstallmentId());
            List<MerInstallmentInfoCompetitive> listNew = merInstallmentInfoCompetitiveService.list(Wrappers.<MerInstallmentInfoCompetitive>lambdaQuery().eq(MerInstallmentInfoCompetitive::getInstallmentId, installmentInfo.getInstallmentId()).orderByAsc(MerInstallmentInfoCompetitive::getCompetitiveOrganizations));
            //记录竞品机构修改日志
            saveInstallmentInfoLog(listOld, listNew, dto.getMerId());


        }

    }

    private void saveInstallmentInfoLog(List<MerInstallmentInfoCompetitive> listOld, List<MerInstallmentInfoCompetitive> listNew, String merId) {

        String oldCompetitiveSummary = listOld.stream()
                .map(item -> (item.getCompetitiveOrganizations() == null ? "" : merParamsService.translateParamKeys(item.getCompetitiveOrganizations())) + ":" + (item.getCompetitiveRate() == null ? "" : item.getCompetitiveRate().toString()))
                .collect(Collectors.joining(","));
        // competitiveSummary 变量可用于后续日志、展示等
        String newCompetitiveSummary = listNew.stream()
                .map(item -> (item.getCompetitiveOrganizations() == null ? "" : merParamsService.translateParamKeys(item.getCompetitiveOrganizations())) + ":" + (item.getCompetitiveRate() == null ? "" : item.getCompetitiveRate().toString()))
                .collect(Collectors.joining(","));
        if (!oldCompetitiveSummary.equals(newCompetitiveSummary)) {
            MerMerchantAuditEditLog log = new MerMerchantAuditEditLog();
            log.setBeforeContent(oldCompetitiveSummary);
            log.setAfterContent(newCompetitiveSummary);
            log.setMerId(merId);
            log.setModule(CommonEnum.merchantEditLogType.INSTALLMENT_INFO.name());
            merMerchantAuditEditLogService.save(log);


        }
    }

    @Override
    public Page<MerchantAuditPageVo> listPage(MerchantAuditManageDto dto) {
        String userId = SecurityUtils.getUserId();
        //todo 如果是领导则不带当前登录人员id
        Page<MerchantAuditPageVo> pageVO = merchantAuditManageMapper.listPage(dto.page(), dto, userId);

        pageVO.getRecords().forEach(vo -> {
            String brandTypeDesc = "";
            if (StringUtils.hasText(vo.getBrandType())) {
                CommonEnum.brandType brandTypeEnum = CommonEnum.brandType.getBrandTypeByCode(vo.getBrandType());
                if (brandTypeEnum != null) {
                    brandTypeDesc = brandTypeEnum.getDescription();
                }
            }

            String storeTypeDesc = "";
            if (StringUtils.hasText(vo.getStoreType())) {
                CommonEnum.storeType storeTypeEnum = CommonEnum.storeType.getStoreTypeByCode(vo.getStoreType());
                if (storeTypeEnum != null) {
                    storeTypeDesc = storeTypeEnum.getDescription();
                }
            }

            List<String> parts = new ArrayList<>();
            if (StringUtils.hasText(brandTypeDesc)) {
                parts.add(brandTypeDesc);
            }
            if (StringUtils.hasText(storeTypeDesc)) {
                parts.add(storeTypeDesc);
            }
            vo.setSceneCategory(processSceneCategory(vo.getSceneCategory()));
            vo.setBrandStoreName(String.join("|", parts));
            vo.setSceneName(getSceneName(merParamsService.translateParamKeys(vo.getSceneParentCategory()), merParamsService.translateParamKeys(vo.getSceneCategory())));
            vo.setRefusedReasonName(merParamsService.translateParamKeys(vo.getRefusedReason()));

        });

        return pageVO;
    }

    public String processSceneCategory(String sceneCategory) {
        // 检查字符串是否为空
        if (sceneCategory == null || sceneCategory.isEmpty()) {
            return sceneCategory;
        }

        // 查找|符号的位置
        int index = sceneCategory.indexOf("|");

        // 如果存在|符号，则截取其前面的部分；否则返回原字符串
        if (index != -1) {
            return sceneCategory.substring(0, index);
        } else {
            return sceneCategory;
        }
    }

    @Override
    public MerchantApplyCountVo countByAuthStatus(MerchantAuditManageDto dto) {
        return merchantAuditManageMapper.countByAuthStatus(dto, SecurityUtils.getUserId());

    }

    @Override
    public List<MerchantAuditEditPageVo> auditEditPage(MerMerchantAuditEditLogDto dto) {
        QueryWrapper<MerMerchantAuditEditLog> wrapper = new QueryWrapper<>();
        wrapper.eq("mer_id", dto.getMerId())
                .orderByDesc("add_time", "after_content");
        List<MerMerchantAuditEditLog> list = merMerchantAuditEditLogMapper.selectList(wrapper);
        return list.stream().map(log -> {
            MerchantAuditEditPageVo vo = new MerchantAuditEditPageVo();
            vo.setMerId(log.getMerId());
            vo.setBeforeContent((log.getBeforeContent() == null || "".equals(log.getBeforeContent())) ? "新增" : log.getBeforeContent());
            vo.setAfterContent((log.getAfterContent() == null || "".equals(log.getAfterContent())) ? "删除" : log.getAfterContent());
            vo.setAddTime(log.getAddTime() != null ? log.getAddTime() : null);
            vo.setAddEmpID(String.valueOf(log.getAddEmpId()));
            vo.setAddEmpName(log.getAddEmpName());
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<MerAuthFlowLogPageVo> flowPage(MerAuthFlowLogDto dto) {
        // 先查出mer_id对应的所有flow_id
        QueryWrapper<MerAuthFlow> flowWrapper = new QueryWrapper<>();
        flowWrapper.eq("mer_id", dto.getMerId());
        List<MerAuthFlow> flows = merAuthFlowService.list(flowWrapper);
        if (flows.isEmpty()) return new ArrayList<>();
        List<String> flowIds = flows.stream().map(MerAuthFlow::getFlowId).collect(Collectors.toList());
        QueryWrapper<MerAuthFlowLog> logWrapper = new QueryWrapper<>();
        logWrapper.in("flow_id", flowIds).orderByDesc("auth_date");
        List<MerAuthFlowLog> logs = merAuthFlowLogService.list(logWrapper);
        return logs.stream().map(log -> {
            MerAuthFlowLogPageVo vo = new MerAuthFlowLogPageVo();
            vo.setLogId(log.getLogId());
            vo.setFlowId(log.getFlowId());
            vo.setAuthMan(log.getAuthMan());
            vo.setAuthStatus(log.getAuthStatus());
            CommonEnum.auditType type = CommonEnum.auditType.fromDescription(log.getAuthStatus());
            vo.setAuthStatusName(type != null ? type.getDescription() : "");
            vo.setAuthRemark(log.getAuthRemark());
            vo.setAuthDate(log.getAuthDate() != null ? log.getAuthDate() : null);
            return vo;
        }).collect(Collectors.toList());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void merchantAudit(@Validated MerchantAuditDto dto) {
        //参数校验
        // step 1: 流程表下结论
        MerAuthFlow merAuthFlow = merAuthFlowService.getOne(Wrappers.<MerAuthFlow>lambdaQuery().eq(MerAuthFlow::getMerId, dto.getMerId()));
        String lastStatue = merAuthFlow.getAuthStatus();
        BeanUtils.copyPropertiesIgnoreNull(dto, merAuthFlow);
        // step 2: 审核记录日志保存
        MerAuthFlowLog merAuthFlowLog = getMerAuthFlowLog(merAuthFlow, lastStatue, dto.getRemarks());
        merAuthFlowLogService.save(merAuthFlowLog);
        //step  4:附件照片下结论
        dto.getMerAttachDtoList().forEach(merAttachDto -> {
            MerAttach merAttach = merAttachService.getById(merAttachDto.getAttachId());
            BeanUtils.copyPropertiesIgnoreNull(merAttachDto, merAttach);
            merAttach.setRemark(merAttachDto.getAttachRemark());
            merAttachService.updateById(merAttach);
        });

        //step  5:更新商户表审核时间
        MerMerchant merchant = merMerchantService.getById(dto.getMerId());
        BeanUtils.copyPropertiesIgnoreNull(dto, merchant);
        if (!StringUtils.hasText(dto.getAuthStatus())) {
            throw BusinessException.businessOther("请下结论");
        }
        //step 6: 如果是补件结论需要翻转流程表为补件
        if (dto.getAuthStatus().equals(CommonEnum.auditType.SUPPLEMENTARY_PARTS.name())) {
            //补件次数最多两次
            if (merAuthFlow.getSupplementNum() > 1) {
                throw BusinessException.businessOther("当前案件补件次数已超过2次，不允许补件");
            }

            dto.getMerAttachDtoList().stream()
                    .filter(attach -> StringUtils.isEmpty(attach.getAttachResult()))
                    .findAny()
                    .ifPresent(attach -> {
                        throw BusinessException.businessOther("附件" + attach.getConfigName() + "未下结论");
                    });

            //没有补件的附件，则不允许下补件结论
            List<MerAttachDto> bujianList = Optional.ofNullable(dto.getMerAttachDtoList())
                    .map(list -> list.stream()
                            .filter(a -> "补件".equals(a.getAttachResult()))
                            .collect(Collectors.toList()))
                    .filter(list -> !list.isEmpty())
                    .orElseThrow(() -> BusinessException.businessOther("没有需要补件的数据"));


            if (merAuthFlow.getSupplementNum() > 0) {
                bujianList.stream()
                        .filter(attach -> StringUtils.isEmpty(attach.getAttachRemark()))
                        .findAny()
                        .ifPresent(attach -> {
                            throw BusinessException.businessOther("请补充" + attach.getConfigName() + "附件的补件原因");
                        });
            } else {
                bujianList.stream()
                        .filter(attach -> StringUtils.isEmpty(attach.getAttachRemark()) && attach.getAttachType() != 2)
                        .findAny()
                        .ifPresent(attach -> {
                            throw BusinessException.businessOther("请补充" + attach.getConfigName() + "附件的补件原因");
                        });
            }

            //更新补件时间、
            //merAuthFlow.setSupplementTime(LocalDateTime.now());

            //生成补件步骤
            merMerchantRegisterStepService.createSupplementaryPartsFlow(dto.getMerId());
        }
        //step 7:如果是通过结论，需要更新审核结论字段，更新商户表，以及验证附件是否全部下了结论
        if (dto.getAuthStatus().equals(CommonEnum.auditType.PENDING_SIGNING.name())) {
            //结论参数校验
            checkSubmitParam(dto);
            //去所有附件最新的结论数据，如果有非合格的数据，则提示未全部审核
            List<MerAttachVO> attachList = merAttachService.getLatestAttachByMerId(dto.getMerId());
            List<MerAttachVO> notProcessed = attachList.stream()
                    .filter(x -> x.getAttachResult() == null || !"合格".equals(x.getAttachResult()))
                    .collect(Collectors.toList());
            if (!notProcessed.isEmpty()) {
                throw BusinessException.businessParamsNotExist(notProcessed.get(0).getConfigName() + ":未下结论，请确认");
            }
            createMerchantHistoryRecord(dto.getMerId());
        }
        //更新商户状态，流程表状态
        merMerchantService.updateById(merchant);
        merAuthFlow.setAuthEndDate(LocalDateTime.now());
        merAuthFlowService.updateById(merAuthFlow);

    }

    private void createMerchantHistoryRecord(String merId) {
        MerCooperateHistory entity = new MerCooperateHistory();
        entity.setMerId(merId);
        entity.setHistoricalEvents("复审通过");
        merCooperateHistoryService.save(entity);
    }


    //修改资方顺位信息
    @Override
    public void editCapitalOrder(List<MerMerchantCapitalOrderDto> vo) {
        for (MerMerchantCapitalOrderDto dto : vo) {
            MerMerchantCapitalOrder merMerchantCapitalOrder = merMerchantCapitalOrderService.getById(dto.getCapitalId());
            MerMerchantCapitalOrder oldDto = new MerMerchantCapitalOrder();
            BeanUtils.copyPropertiesIgnoreNull(merMerchantCapitalOrder, oldDto);
            BeanUtils.copyPropertiesIgnoreNull(dto, merMerchantCapitalOrder);
            merMerchantCapitalOrderService.updateById(merMerchantCapitalOrder);
            saveLog(merMerchantCapitalOrder, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.CAPITAL_ORDER.name());
        }
    }

    @Override
    public JSONObject getMerchantLevelAndAmount(String merId) {
        return riskEgService.submitRiskEg(merId, CommonEnum.riskControlEngineType.OBTAIN_MERCHANT_LEVEL);
    }

    @Override
    public void editMedicalEduLicense(MedicalEduLicenseDto dto) {
        MerMedicalEduLicense merMedicalEduLicense = merMedicalEduLicenseService.getOne(Wrappers.<MerMedicalEduLicense>lambdaQuery().eq(MerMedicalEduLicense::getMerId, dto.getMerId()));
        if (merMedicalEduLicense == null) {
            throw BusinessException.businessDataNotExist("该商户没有教育办学信息");
        }
        if (StringUtils.hasText(dto.getValidateStart())) {
            LocalDate startTime = LocalDate.parse(dto.getValidateStart());
            LocalDate now = LocalDate.now();
            if (startTime.isAfter(now)) {
                throw BusinessException.businessValidateError("办学许可证执业有效期开始日期不能大于今天");
            }
        }
        if (StringUtils.hasText(dto.getValidateEnd()) && !Objects.equals("长期", dto.getValidateEnd())) {
            LocalDate endTime = LocalDate.parse(dto.getValidateEnd());
            LocalDate now = LocalDate.now();
            if (endTime.isBefore(now)) {
                throw BusinessException.businessValidateError("办学许可证执业有效期结束日期不能小于今天");
            }
        }

        MerMedicalEduLicense oldDto = new MerMedicalEduLicense();
        BeanUtils.copyPropertiesIgnoreNull(merMedicalEduLicense, oldDto);
        BeanUtils.copyPropertiesIgnoreNull(dto, merMedicalEduLicense);
        merMedicalEduLicenseService.updateById(merMedicalEduLicense);
        saveLog(merMedicalEduLicense, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.PRACTICE_LICENSE.name());


    }

    @Override
    public void editMedicalEduIcpLicense(MedicalEduIcpLicenseDto dto) {
        MerMedicalEduIcpLicense merMedicalEduIcpLicense = merMedicalEduIcpLicenseService.getOne(Wrappers.<MerMedicalEduIcpLicense>lambdaQuery().eq(MerMedicalEduIcpLicense::getMerId, dto.getMerId()));
        if (merMedicalEduIcpLicense == null) {
            throw BusinessException.businessDataNotExist("该商户没有教育机构信息");
        }
        if (StringUtils.hasText(dto.getValidateStart())) {
            LocalDate startTime = LocalDate.parse(dto.getValidateStart());
            LocalDate now = LocalDate.now();
            if (startTime.isAfter(now)) {
                throw BusinessException.businessValidateError("ICP有效期开始日期不能大于今天");
            }
        }
        if (StringUtils.hasText(dto.getValidateEnd()) && !Objects.equals("长期", dto.getValidateEnd())) {
            LocalDate endTime = LocalDate.parse(dto.getValidateEnd());
            LocalDate now = LocalDate.now();
            if (endTime.isBefore(now)) {
                throw BusinessException.businessValidateError("ICP有效期结束日期不能小于今天");
            }
        }
        MerMedicalEduIcpLicense oldDto = new MerMedicalEduIcpLicense();
        BeanUtils.copyPropertiesIgnoreNull(merMedicalEduIcpLicense, oldDto);
        BeanUtils.copyPropertiesIgnoreNull(dto, merMedicalEduIcpLicense);
        merMedicalEduIcpLicenseService.updateById(merMedicalEduIcpLicense);
        saveLog(merMedicalEduIcpLicense, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.PRACTICE_LICENSE.name());
    }

    @Override
    public void editBackCardListInfo(List<MerBackCardDto> backCardDtos) {
        for (MerBackCardDto dto : backCardDtos) {
            MerBankCard merBankCard = merBankCardService.getById(dto.getCardId());
            MerBankCard oldDto = new MerBankCard();
            BeanUtils.copyPropertiesIgnoreNull(merBankCard, oldDto);
            BeanUtils.copyPropertiesIgnoreNull(dto, merBankCard);
            merBankCardService.updateById(merBankCard);
            saveLog(merBankCard, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.RECIPIENT_INFO.name());
        }
    }

    @Override
    @Transactional
    public void editMerchantRateInfo(List<MerchantRateDto> merchantRateDtos) {
        if (CollectionUtils.isEmpty(merchantRateDtos)) {
            merMerchantRateManageService.remove(Wrappers.<MerMerchantRateManage>lambdaQuery().eq(MerMerchantRateManage::getMerId, merchantRateDtos.get(0).getMerId()));
            return;
        }
        //取出传入的数据中存在主键的
        List<String> rateInfoList = merchantRateDtos.stream()
                .map(MerchantRateDto::getMerRateId)
                .filter(StringUtils::hasText)
                .collect(Collectors.toList());
        List<MerMerchantRateManage> list = merMerchantRateManageService.list(Wrappers.<MerMerchantRateManage>lambdaQuery().eq(MerMerchantRateManage::getMerId, merchantRateDtos.get(0).getMerId()));
        for (MerMerchantRateManage dto : list) {
            if (!rateInfoList.contains(dto.getMerRateId())) {
                merMerchantRateManageService.removeById(dto);
                saveLog(new MerMerchantRateManage(), dto, dto.getMerId(), CommonEnum.merchantEditLogType.RATE_INFO.name());
            }
        }
        for (MerchantRateDto dto : merchantRateDtos) {
            if (dto.getMerRateId() == null) {
                MerMerchantRateManage newDto = new MerMerchantRateManage();
                BeanUtils.copyPropertiesIgnoreNull(dto, newDto);
                merMerchantRateManageService.save(newDto);
                batchSaveOrUpdateNew(dto, newDto.getMerRateId());
                saveLog(newDto, new MerMerchantRateManage(), dto.getMerId(), CommonEnum.merchantEditLogType.RATE_INFO.name());
            } else {
                MerMerchantRateManage merchantRate = merMerchantRateManageService.getById(dto.getMerRateId());
                MerchantRateDto oldDto = new MerchantRateDto();
                BeanUtils.copyPropertiesIgnoreNull(merchantRate, oldDto);
                MerMerchantRateManage olderDto = new MerMerchantRateManage();
                BeanUtils.copyPropertiesIgnoreNull(merchantRate, olderDto);
                List<MerMerchantRateManageDetail> rateManageDetails = merMerchantRateManageDetailService.list(Wrappers.<MerMerchantRateManageDetail>lambdaQuery()
                        .eq(MerMerchantRateManageDetail::getMerRateId, merchantRate.getMerRateId()));
                List<MerchantRateDetailDto> details = new ArrayList<>();
                rateManageDetails.forEach(detail -> {
                    MerchantRateDetailDto convert = Convert.convert(MerchantRateDetailDto.class, detail);
                    details.add(convert);
                });
                merMerchantRateManageService.populatePhaseData(oldDto, details);
                BeanUtils.copyPropertiesIgnoreNull(dto, merchantRate);
                merMerchantRateManageService.updateById(merchantRate);
                batchSaveOrUpdateNew(dto, merchantRate.getMerRateId());
                saveLog(merchantRate, olderDto, dto.getMerId(), CommonEnum.merchantEditLogType.RATE_INFO.name());
            }
        }
    }


    private static MerAuthFlowLog getMerAuthFlowLog(MerAuthFlow merAuthFlow, String lastStatue, String remarks) {
        MerAuthFlowLog merAuthFlowLog = new MerAuthFlowLog();
        merAuthFlowLog.setFlowId(merAuthFlow.getFlowId());
        merAuthFlowLog.setAuthStatus(merAuthFlow.getAuthStatus());
        merAuthFlowLog.setLastAuthStatus(lastStatue);
        merAuthFlowLog.setAuthRemark(remarks);
        merAuthFlowLog.setAuthManId(SecurityUtils.getUserId());
        merAuthFlowLog.setAuthMan(SecurityUtils.getUsername());
        merAuthFlowLog.setAuthDate(LocalDateTime.now());
        return merAuthFlowLog;
    }

    private MerchantAuditDetailVo getMerchantAuditDetail(String merId) {
        //当前流程审核状态,如果是待审核，则分配当前UM为审核人，并更新当前入审时间
        MerAuthFlow flow = merAuthFlowService.getOne(Wrappers.<MerAuthFlow>lambdaQuery().eq(MerAuthFlow::getMerId, merId));
        if (flow == null) {
            return null;
        }
        //流程表数据
        MerchantAuditDetailVo merchantAuditDetailVo = new MerchantAuditDetailVo();
        if (CommonEnum.auditType.SUPPLEMENTARY_PARTS.name().equals(flow.getAuthStatus()) ||
                CommonEnum.auditType.REVIEW_REFUSED.name().equals(flow.getAuthStatus()) ||
                CommonEnum.auditType.REVIEW_RETURNED.name().equals(flow.getAuthStatus()) ||
                CommonEnum.auditType.PENDING_SIGNING.name().equals(flow.getAuthStatus())
        ) {
            merchantAuditDetailVo.setAuthResult(flow.getAuthStatus());
            merchantAuditDetailVo.setAuthResultName(CommonEnum.auditType.fromDescription(flow.getAuthStatus()).getDescription());
        }


        merchantAuditDetailVo.setApplyTime(
                Optional.ofNullable(flow.getApplyTime())

                        .orElse(null)
        );
        merchantAuditDetailVo.setAuthEndDate(flow.getAuthEndDate());
        merchantAuditDetailVo.setAuthStatus(flow.getAuthStatus());
        merchantAuditDetailVo.setRefusedReason(flow.getRefusedReason());
        merchantAuditDetailVo.setRefusedReasonName(merParamsService.translateParamKeys(flow.getRefusedReason()));
        merchantAuditDetailVo.setRefusedRemark(flow.getRefusedRemark());
        merchantAuditDetailVo.setRemarks(flow.getRemarks());
        merchantAuditDetailVo.setAuditPolicyName(merParamsService.translateParamKeys(flow.getAuditPolicy()));
        merchantAuditDetailVo.setLbsFlag(flow.getLbsFlag());
        merchantAuditDetailVo.setAmountLimitType(flow.getAmountLimitType());
        merchantAuditDetailVo.setDailyLimit(flow.getDailyLimit());
        merchantAuditDetailVo.setMonthlyLimit(flow.getMonthlyLimit());
        //将限额方式，日限额，月限额字段拼接
        merchantAuditDetailVo.setAmountLimitDesc(this.getAmountLimitDesc(flow.getAmountLimitType(), flow.getDailyLimit(), flow.getMonthlyLimit()));
        merchantAuditDetailVo.setContractAward(flow.getContractAward());
        merchantAuditDetailVo.setSupplementNum(flow.getSupplementNum());
        merchantAuditDetailVo.setMerId(merId);
        MerMerchant merchant = merMerchantService.getById(merId);
        if (merchant == null) {
            return null;
        }
        //商户表数据
        merchantAuditDetailVo.setSpecialApply(merchant.getSpecialApply());
        merchantAuditDetailVo.setApplyCode(merchant.getApplyCode());
        merchantAuditDetailVo.setSceneCategory(merchant.getSceneCategory());
        merchantAuditDetailVo.setSceneCategoryName(merParamsService.translateParamKeys(merchant.getSceneCategory()));
        merchantAuditDetailVo.setSceneParentCategory(merchant.getSceneParentCategory());
        merchantAuditDetailVo.setSceneParentCategoryName(merParamsService.translateParamKeys(merchant.getSceneParentCategory()));
        merchantAuditDetailVo.setSceneName(getSceneName(merchantAuditDetailVo.getSceneParentCategoryName(), merchantAuditDetailVo.getSceneCategoryName()));
        merchantAuditDetailVo.setApplyDesc(merchant.getApplyDesc());
        merchantAuditDetailVo.setBrandName(merParamsService.translateParamKeys(merchant.getBrandName()));
        merchantAuditDetailVo.setApplyPolicy(merchant.getApplyPolicy());
        merchantAuditDetailVo.setApplyPolicyName(merParamsService.translateParamKeys(merchant.getApplyPolicy()));


        // 码值表设置申请类型名称
        merchantAuditDetailVo.setApplyType(merchant.getApplyType());
        merchantAuditDetailVo.setApplyTypeName(
                Optional.ofNullable(merchant.getApplyType())
                        .map(CommonEnum.applyType::getApplyTypeByCode)
                        .map(CommonEnum.applyType::getDescription)
                        .orElse(merchant.getApplyType())
        );

        // 码值表设置店铺类型名称
        merchantAuditDetailVo.setStoreType(merchant.getStoreType());
        merchantAuditDetailVo.setStoreTypeName(
                Optional.ofNullable(merchant.getStoreType())
                        .map(CommonEnum.storeType::getStoreTypeByCode)
                        .map(CommonEnum.storeType::getDescription)
                        .orElse(merchant.getStoreType())
        );

        // 码值表设置品牌类型名称
        merchantAuditDetailVo.setBrandType(merchant.getBrandType());
        merchantAuditDetailVo.setBrandTypeName(
                Optional.ofNullable(merchant.getBrandType())
                        .map(CommonEnum.brandType::getBrandTypeByCode)
                        .map(CommonEnum.brandType::getDescription)
                        .orElse(merchant.getBrandType())
        );

        // 码值表设置获客方式名称
        if (StringUtils.hasText(merchant.getCustomerAcquisitionMethod())) {
            merchantAuditDetailVo.setCustomerAcquisitionMethod(merchant.getCustomerAcquisitionMethod());
            String customerAcquisitionMethod = merchant.getCustomerAcquisitionMethod();
            List<String> list = Arrays.asList(customerAcquisitionMethod.split(","));
            merchantAuditDetailVo.setCustomerAcquisitionMethodDesc(
                    list.stream()
                            .map(CommonEnum.customerAcquisitionMethodType::getByCode).filter(Objects::nonNull)
                            .map(CommonEnum.customerAcquisitionMethodType::getDescription)
                            .collect(Collectors.joining(","))
            );
        }

        merchantAuditDetailVo.setMerName(merchant.getMerName());


        merchantAuditDetailVo.setMerModel(merchant.getMerModel());
        merchantAuditDetailVo.setTrafficMerchant(merchant.getTrafficMerchant());
        merchantAuditDetailVo.setRelatedMerId(merchant.getRelatedMerId());


        merchantAuditDetailVo.setNegativeInformation(null);
        MerLicense merLicense = merLicenseService.getOne(Wrappers.<MerLicense>lambdaQuery().eq(MerLicense::getMerId, merId));
        if (merLicense != null) {
            merchantAuditDetailVo.setBusinessStatus(merLicense.getBusinessStatus());

        }
        merchantAuditDetailVo.setMerLevel(merchant.getMerLevel());
        merchantAuditDetailVo.setMerchantCreditLimit(merchant.getMerchantCreditLimit());
        merchantAuditDetailVo.setLabel(merchant.getLabel());
        merchantAuditDetailVo.setLabelName(merParamsService.translateParamKeys(merchant.getLabel()));

        MerMerchant relatedMerchant = merMerchantService.getById(merchant.getRelatedMerId());
        if (relatedMerchant != null) {
            merchantAuditDetailVo.setRelatedMerName(relatedMerchant.getMerName());
        }


        return merchantAuditDetailVo;
    }

    //parentCategory为A，childCategories为B|C,B|D，则合并为字符串A|B|C,A|B|D
    private String getSceneName(String parentCategory, String childCategories) {
        if (parentCategory == null || parentCategory.isEmpty()) {
            return childCategories;
        }

        if (childCategories == null || childCategories.isEmpty()) {
            return parentCategory;
        }

        return Arrays.stream(childCategories.split(","))
                .map(group -> parentCategory + "|" + group)
                .collect(Collectors.joining(","));

    }

    private String prependIfNotEmpty(String prefix, String content) {
        return StringUtils.hasText(content) ? prefix + content : content;
    }

    private <T> void saveLog(T newDto, T oldDto, String merId, String module) {
        MerMerchantAuditEditLog log = new MerMerchantAuditEditLog();
        Map<String, List<String>> diffValue = historyDiffUtils.getDiffValue(newDto, oldDto);
        log.setMerId(merId);
        if ((diffValue.get("old") == null || diffValue.get("old").isEmpty()) && (diffValue.get("new") == null || diffValue.get("new").isEmpty())) {
            return;
        }
        String beforeContent = String.join("，", diffValue.get("old"));
        String afterContent = String.join("，", diffValue.get("new"));
        // 策略处理前缀
        String prefix = LOG_PREFIX_STRATEGY.getOrDefault(oldDto != null ? oldDto.getClass() : null, (a, b) -> "").apply(oldDto, newDto);
        log.setBeforeContent(prependIfNotEmpty(prefix, beforeContent));
        log.setAfterContent(prependIfNotEmpty(prefix, afterContent));
        log.setModule(module);
        merMerchantAuditEditLogService.save(log);
    }


    /**
     * 商户审核参数校验
     */
    private void checkSubmitParam(MerchantAuditDto dto) {
        if (!StringUtils.hasText(dto.getMerId())) {
            throw BusinessException.businessParamsNotExist("流程ID不能为空");
        }
        if (!StringUtils.hasText(dto.getAuthStatus())) {
            throw BusinessException.businessParamsNotExist("审核结论authStatus)不能为空");
        }
        if (!StringUtils.hasText(dto.getApplyPolicy())) {
            throw BusinessException.businessParamsNotExist("申请政策不能为空");
        }
        if (!StringUtils.hasText(dto.getAuditPolicy())) {
            throw BusinessException.businessParamsNotExist("审批政策不能为空");
        }
        if (!StringUtils.hasText(dto.getLabel())) {
            throw BusinessException.businessParamsNotExist("商户标签不能为空");
        }
        if (dto.getLbsFlag() == null) {
            throw BusinessException.businessParamsNotExist("lbs验证不能为空");
        }
        if (dto.getContractAward() == null) {
            throw BusinessException.businessParamsNotExist("合同签约不能为空");
        }
        if (!StringUtils.hasText(dto.getAmountLimitType())) {
            throw BusinessException.businessParamsNotExist("限额方式不能为空");
        }
        if (dto.getAmountLimitType().contains("日限额") && dto.getDailyLimit() == null) {
            throw BusinessException.businessParamsNotExist("日限额不能为空");
        }
        if (dto.getAmountLimitType().contains("月限额") && dto.getMonthlyLimit() == null) {
            throw BusinessException.businessParamsNotExist("月限额不能为空");
        }
        if (!StringUtils.hasText(dto.getRemarks())) {
            throw BusinessException.businessParamsNotExist("审核意见不能为空");
        }

    }

    public String getAmountLimitDesc(String amountLimitType, BigDecimal dailyLimit, BigDecimal monthlyLimit) {
        return Optional.ofNullable(amountLimitType)
                .map(type -> {
                    if ("日限额".equals(type)) {
                        return "日限额" + dailyLimit;
                    } else if ("月限额".equals(type)) {
                        return "月限额" + monthlyLimit;
                    } else if (type.contains("日限额") && type.contains("月限额")) {
                        return "日限额" + dailyLimit + "，月限额" + monthlyLimit;
                    } else {
                        return amountLimitType;
                    }
                })
                .orElse("");


    }

    public void batchSaveOrUpdateNew(MerchantRateDto dto, String merRateId) {
        List<MerchantRateDetailDto> details = new ArrayList<>();
        if (dto.getPhaseThere() != null) {
            details.add(dto.getPhaseThere());
        }
        if (dto.getPhaseSix() != null) {
            details.add(dto.getPhaseSix());
        }
        if (dto.getPhaseNine() != null) {
            details.add(dto.getPhaseNine());
        }
        if (dto.getPhaseTwelve() != null) {
            details.add(dto.getPhaseTwelve());
        }
        if (dto.getPhaseEighteen() != null) {
            details.add(dto.getPhaseEighteen());
        }
        if (dto.getPhaseTwentyFour() != null) {
            details.add(dto.getPhaseTwentyFour());
        }
        if (dto.getPhaseThirtySix() != null) {
            details.add(dto.getPhaseThirtySix());
        }
        if (CollectionUtils.isEmpty(details)) {
            return;
        }
        details.forEach(item -> {
            if (StringUtils.hasText(item.getRateDetailId())) {
                MerMerchantRateManageDetail merMerchantRateManageDetail = merMerchantRateManageDetailService.getById(item.getRateDetailId());
                MerMerchantRateManageDetail oldDto = new MerMerchantRateManageDetail();
                BeanUtils.copyPropertiesIgnoreNull(merMerchantRateManageDetail, oldDto);
                BeanUtils.copyPropertiesIgnoreNull(item, merMerchantRateManageDetail);
                merMerchantRateManageDetail.setMerRateId(merRateId);
                merMerchantRateManageDetail.setEnableFlag(true);
                merMerchantRateManageDetailService.updateById(merMerchantRateManageDetail);
                saveLog(merMerchantRateManageDetail, oldDto, dto.getMerId(), CommonEnum.merchantEditLogType.RATE_INFO.name());
            } else {
                MerMerchantRateManageDetail merMerchantRateManageDetail = new MerMerchantRateManageDetail();
                BeanUtils.copyPropertiesIgnoreNull(item, merMerchantRateManageDetail);
                merMerchantRateManageDetail.setMerRateId(merRateId);
                merMerchantRateManageDetail.setEnableFlag(true);
                merMerchantRateManageDetailService.save(merMerchantRateManageDetail);
                //记录日志
                MerMerchantRateManageDetail newDto = new MerMerchantRateManageDetail();
                newDto.setMerRateId(merMerchantRateManageDetail.getMerRateId());
                newDto.setRateDetailId(merMerchantRateManageDetail.getRateDetailId());
                newDto.setTimes(merMerchantRateManageDetail.getTimes());
                saveLog(merMerchantRateManageDetail, newDto, dto.getMerId(), CommonEnum.merchantEditLogType.RATE_INFO.name());

            }
        });
    }
}

